﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Task_When_Start.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon.Threading
{
    using System;
    using System.Threading;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Testing;
    using Testing.Threading;
    using Mocks;

    [TestClass]
    public class Task_When_Start : AsynchronousTest
    {
        [TestMethod]
        public void If_Is_Already_Started_Should_Throw_InvalidOperationException()
        {
            var taskRan = 0;
            var task = TestTaskFactories.Synchronous.From(() => taskRan++);
            task.Start();

            // verify calling start more than once throws an InvalidOperationException
            AssertException.Throws<InvalidOperationException>(task.Start);

            // verify task was run once
            Assert.AreEqual(1, taskRan);
        }

        [TestMethod]
        public void If_Is_Continuation_Should_Throw_InvalidOperationException()
        {
            // setup initial task
            var task = TestTaskFactories.Synchronous.From(
                () => Log.Append("1"));
            var taskContinuation = task.ContinueWith(
                () => Log.Append("2"), TestTaskFactories.Synchronous);

            // verify calling start on a continuation throws an InvalidOperationException
            AssertException.Throws<InvalidOperationException>(taskContinuation.Start);
            Assert.AreEqual(string.Empty, Log.Text);
        }

        [TestMethod]
        public void If_Callback_Is_Not_Null_Should_Invoke_Callback_On_Completion()
        {
            // setup initial task
            TestTaskFactories.Synchronous.From(
                () => Log.Append("1")).Start(t => Log.Append("2"));
            Assert.AreEqual("12", Log.Text);
        }

        [TestMethod]
        public void If_Callback_Is_Null_Should_Ignore()
        {
            // setup initial task
            TestTaskFactories.Synchronous.From(
                () => Log.Append("1")).Start();
            Assert.AreEqual("1", Log.Text);
        }

        [TestMethod]
        [HostType("CHESS")]
        public void If_Start_Is_Called_On_Multiple_Threads_Should_Throw_InvalidOperationException_On_All_But_One_Threads()
        {
            var task = Task.Factory.From(() => MockDelegates.DoNothing);

            // create an action to determine the distribution of start throw vs no throw
            var numberThrown = 0;
            var numberNotThrown = 0;
            var isWrongException = false;
            Action action = () =>
                {
                    try
                    {
                        task.Start();
                        Interlocked.Increment(ref numberNotThrown);
                    }
                    catch (InvalidOperationException)
                    {
                        Interlocked.Increment(ref numberThrown);
                    }
                    catch
                    {
                        isWrongException = true;
                        Interlocked.Increment(ref numberThrown);
                    }
                };

            // spin off on 2 threads
            ThreadPool.QueueUserWorkItem(s => action());
            action();

            // wait
            AsyncUtility.SleepUntil(() => numberNotThrown + numberThrown == 2);

            // verify distribution and exception type
            Assert.AreEqual(1, numberNotThrown);
            Assert.IsFalse(isWrongException);
        }
    }
}